package be.kuleuven.cs.mop.domain.model.impl;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import be.kuleuven.cs.mop.domain.model.InvitationManager;
import be.kuleuven.cs.mop.domain.repositories.Repository;
import be.kuleuven.cs.mop.domain.repositories.impl.RepositoryImpl;



/**
 * Manages all the data of the entire system
 */
public class World {
	
	private final Clock						clock;
	private final InvitationManagerImpl			invitationManager	= new InvitationManagerImpl();
	private final Repository<ProjectImpl>	projects;
	private final Repository<ResourceImpl>	resources;
	private final Repository<ResourceTypeImpl> resourceTypes;
	private final Repository<TaskTypeImpl> taskTypes;
	private final Repository<UserImpl>		users;
	private final Repository<UserTypeImpl> userTypes;
	
	/**
	 * Make a new World, as Time, the current time is used.
	 *
	 */
	public World() {
		this(new Clock());
	}
	/**
	 * Make a new World with the given {@link Clock}.
	 * @param time
	 */
	public World(final Clock time) {
		this(time,
				new RepositoryImpl<ProjectImpl>(),
				new RepositoryImpl<ResourceImpl>(),
				new RepositoryImpl<UserImpl>(),
				new RepositoryImpl<ResourceTypeImpl>(),
				new RepositoryImpl<TaskTypeImpl>(),
				new RepositoryImpl<UserTypeImpl>()
		);
	}
	
	
	/**
	 * Make a new World with the given projects,resources and users.
	 */
	public World(final Clock time,
			final Repository<ProjectImpl> projects,
			final Repository<ResourceImpl> resources,
			final Repository<UserImpl> users,
			final Repository<ResourceTypeImpl> resourceTypes,
			final Repository<TaskTypeImpl> taskTypes,
			final Repository<UserTypeImpl> userTypes) {
		if (time == null)
			throw new NullPointerException("Time == NULL");
		this.clock = time;
		
		this.projects	=
			((projects == null) ? new RepositoryImpl<ProjectImpl>() : projects);
		this.resources	=
			((resources == null) ? new RepositoryImpl<ResourceImpl>() : resources);;
		this.users		=
			((users == null) ? new RepositoryImpl<UserImpl>() : users);
		
		this.resourceTypes	=
			((resourceTypes == null) ? new RepositoryImpl<ResourceTypeImpl>() : resourceTypes);
		this.taskTypes	=
			((taskTypes == null) ? new RepositoryImpl<TaskTypeImpl>() : taskTypes);;
		this.userTypes		=
			((userTypes == null) ? new RepositoryImpl<UserTypeImpl>() : userTypes);;
	}
	
	
	/**
	 * Adds a Project to the World.
	 */
	public void add(final ProjectImpl project) {
		projects.add(project);
	}
	
	/**
	 * Adds a Resource to the World.
	 */
	public void add(final ResourceImpl resource) {
		resources.add(resource);
	}
	
	/**
	 * Adds a User to the World.
	 */
	public void add(final UserImpl user) {
		users.add(user);
	}
	
	/**
	 * Adds a ResourceType to the World.
	 */
	public void add(final ResourceTypeImpl resourceType) {
		resourceTypes.add(resourceType);
	}
	
	/**
	 * Adds a TaskType to the World.
	 */
	public void add(final TaskTypeImpl taskType) {
		taskTypes.add(taskType);
	}
	
	/**
	 * Adds a UserType to the World.
	 */
	public void add(final UserTypeImpl userType) {
		userTypes.add(userType);
	}
	
	/**
	 * Returns the global {@link InvitationManager}
	 */
	public InvitationManagerImpl getInvitationManager() { return invitationManager; }
	
	/**
	 * Returns an unmodifiable list of Projects in this World.
	 */
	public List<ProjectImpl> getProjects() {
		return Collections.unmodifiableList(projects.getList());
	}
	
	/**
	 * Returns an unmodifiable list of Resources in this World.
	 */
	public List<ResourceImpl> getResources() {
		return Collections.unmodifiableList(resources.getList());
	}
	
	/**
	 * Returns an unmodifiable list of Resources in this World.
	 */
	public List<TaskImpl> getTasks() {
		final ArrayList<TaskImpl> tasks = new ArrayList<TaskImpl>();
		for (final UserImpl user : users.getList())
			tasks.addAll(user.getTasks());
		return Collections.unmodifiableList(tasks);
	}
	

	/**
	 * Returns an unmodifiable list of Resource Types in this World.
	 */
	public List<ResourceTypeImpl> getResourceTypes() {
		return Collections.unmodifiableList(resourceTypes.getList());
	}
	
	/**
	 * Returns an unmodifiable list of Task Types in this World.
	 */
	public List<TaskTypeImpl> getTaskTypes() {
		return Collections.unmodifiableList(taskTypes.getList());
	}
	
	/**
	 * Returns an unmodifiable list of Resource Types in this World.
	 */
	public List<UserTypeImpl> getUserTypes() {
		return Collections.unmodifiableList(userTypes.getList());
	}
	
	/**
	 * Returns the clock of this World.
	 */
	public Clock getClock() { return clock; }
	
	/**
	 * Returns an unmodifiable list of Users in this World.
	 */
	public List<UserImpl> getUsers() {
		return Collections.unmodifiableList(users.getList());
	}
	
	/**
	 * Removes a project from this World.
	 */
	public void remove(final ProjectImpl project) {
		projects.remove(project);
	}
	
	/**
	 * Removes a Resource from this World.
	 */
	public void remove(final ResourceImpl resource) {
		resources.remove(resource);
	}
	
	/**
	 * Removes a User from this World.
	 */
	public void remove(final UserImpl user) {
		users.remove(user);
	}
	
	/**
	 * Removes a UserType from this World.
	 */
	public void remove(final UserTypeImpl userType) {
		userTypes.remove(userType);
	}
	
	/**
	 * Removes a TaskType from this World.
	 */
	public void remove(final TaskTypeImpl taskType) {
		taskTypes.remove(taskType);
	}
	
	/**
	 * Removes a ResourceType from this World.
	 */
	public void remove(final ResourceTypeImpl resourceType) {
		resourceTypes.remove(resourceType);
	}
	
	/**
	 * Checks whether the given project is present in this World.
	 */
	public boolean isPresent(final ProjectImpl project) {
		return projects.contains(project);
	}
	
	/**
	 * Checks whether the given resource is present in this World.
	 */
	public boolean isPresent(final ResourceImpl resource) {
		return resources.contains(resource);
	}
	
	/**
	 * Checks whether the given user is present in this World.
	 */
	public boolean isPresent(final UserImpl user) {
		return users.contains(user);
	}
	
	/**
	 * Checks whether the given usertype is present in this World.
	 */
	public boolean isPresent(final UserTypeImpl usertype) {
		return userTypes.contains(usertype);
	}
	
	/**
	 * Checks whether the given tasktype is present in this World.
	 */
	public boolean isPresent(final TaskTypeImpl tasktype) {
		return taskTypes.contains(tasktype);
	}
	
	/**
	 * Checks whether the given resourcetype is present in this World.
	 */
	public boolean isPresent(final ResourceTypeImpl resourcetype) {
		return resourceTypes.contains(resourcetype);
	}
	
	/**
	 * Checks whether the given task is present in this World.
	 */
	public boolean isPresent(final TaskImpl task) {
		return getTasks().contains(task);
	}
	
}